//
//  Shader.cpp
//  glwftry
//
//  Created by 李培根 on 2020/8/27.
//  Copyright © 2020 李培根. All rights reserved.
//

#include <stdio.h>
#include "Shader.hpp"
#include <fstream>
#include <sstream>
#include <iostream>


Shader::Shader(const GLchar* vertexPath, const GLchar *fragmentPath)
{
    std::string vertexCode;
    std::string fragmentCode;
    std::ifstream vShadeFile;
    std::ifstream fShadeFile;
    
    vShadeFile.ios_base::exceptions(std::ifstream::failbit| std::ifstream::badbit);
    fShadeFile.ios_base::exceptions(std::ifstream::failbit| std::ifstream::badbit);
    
    try {
        vShadeFile.open(vertexPath);
        fShadeFile.open(fragmentPath);
        std::stringstream vShaderStream, fShaderStream;
        
        vShaderStream << vShadeFile.rdbuf();
        fShaderStream << fShadeFile.rdbuf();
        
        vShadeFile.close();
        fShadeFile.close();
        
        vertexCode = vShaderStream.str();
        fragmentCode = fShaderStream.str();
        
    } catch (std::ifstream::failure& e) {
        std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ" << std::endl;
    }
    
    const char* vShaderCode = vertexCode.c_str();
    const char* fShaderCode = fragmentCode.c_str();
    
    unsigned int vertex, fragment;
    
    vertex = glad_glCreateShader(GL_VERTEX_SHADER);
    glad_glShaderSource(vertex, 1, &vShaderCode, NULL);
    glad_glCompileShader(vertex);
    
    checkCompileErrors(vertex, "VERTEX");
    
    fragment = glad_glCreateShader(GL_FRAGMENT_SHADER);
    glad_glShaderSource(fragment, 1, &fShaderCode, NULL);
    glad_glCompileShader(fragment);
    checkCompileErrors(fragment, "FRAGMENT");
    
    ID = glad_glCreateProgram();
    glad_glAttachShader(ID, vertex);
    glad_glAttachShader(ID, fragment);
    glad_glLinkProgram(ID);
    checkCompileErrors(ID, "PROGRAM");
    
    glad_glDeleteShader(vertex);
    glad_glDeleteShader(fragment);
}

void Shader::use()
{
    glad_glUseProgram(ID);
}

void Shader::setBool(const std::string &name, bool value) const
{
    glad_glUniform1i(glad_glGetUniformLocation(ID, name.c_str()), (int)value);
}
void Shader::setInt(const std::string &name, int value) const
{
    glad_glUniform1i(glad_glGetUniformLocation(ID, name.c_str()), value);
}
void Shader::setFloat(const std::string &name, float value) const
{
    GLint location = glad_glGetUniformLocation(ID, name.c_str());
    glad_glUniform1f(location, value);
}
    
void Shader::checkCompileErrors(unsigned int shader, std::string type) {
    int success;
    char infoLog[1024];
    if (type != "PROGRAM") {
        glad_glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
        if (!success) {
            glad_glGetShaderInfoLog(shader, 1024, NULL, infoLog);
            std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
        }
        else {
            glGetProgramiv(shader, GL_LINK_STATUS, &success);
            if (!success)
            {
                glGetProgramInfoLog(shader, 1024, NULL, infoLog);
                std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
            }
        }
    }
}
